#pragma once

namespace Honker
{

	template<size_t N>
	struct Apply
	{
		template<typename F, typename T, typename... A>
		static inline auto apply( F && f, T && t, A &&... a )
			-> decltype(Apply<N - 1>::apply(
			::std::forward<F>( f ), ::std::forward<T>( t ),
			::std::forward<A>( a )... ,
			t[ N - 1 ]
			))
		{
			return Apply<N - 1>::apply( ::std::forward<F>( f ),
										::std::forward<T>( t ),
										::std::forward<A>( a )...,
										t[ N - 1 ]
										);
		}
	};

	template<>
	struct Apply < 0 >
	{
		template<typename F, typename T, typename... A>
		static inline auto apply( F && f, T &&, A &&... a )
			-> decltype(::std::forward<F>( f )
			(::std::forward<A>( a )...))
		{
			return ::std::forward<F>( f )(::std::forward < A >
										   ( a )...);
		}
	};
	class DispatchMethodBase
	{
	public:
		virtual HRESULT Execute( WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult ) { return S_OK; };
	};

	template<typename T>
	class DispatchMethod;

	template<typename R, typename...Args>
	class DispatchMethod < R ( Args... ) >
		: public DispatchMethodBase
	{
	public:

		typedef std::function<R ( Args... )> function_type;
		typedef R return_type;
		typedef return_type( *func_ptr )(Args...);
	private:
		typedef std::vector<_variant_t>		params_type;
		function_type				m_fn;

	public:
		DispatchMethod( DispatchMethod&& that ) :m_fn( std::move( that.m_fn ) ) {}
		DispatchMethod( const DispatchMethod& that ) :m_fn( that.m_fn ) {}

		DispatchMethod( function_type&& f ) :m_fn( std::move( f ) ) {}
		DispatchMethod( function_type& f ) :m_fn( f ) {}
		DispatchMethod( func_ptr f ) :m_fn( f ) {}

		~DispatchMethod()
		{}

		DispatchMethod& operator=(const DispatchMethod& _Right)
		{
			if ( this != &_Right )
			{
				this->m_fn = _Right.m_fn;
			}
			return (*this);
		}

		virtual HRESULT Execute( WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult )
		{
			if ( pdispparams->cArgs < sizeof...(Args) )
			{
				return E_INVALIDARG;
			}
			params_type params;
			for ( int i = 0; i < sizeof...(Args); i++ )
			{
				params.push_back( pdispparams->rgvarg[ i ] );
			}
			R r = Apply<sizeof...(Args)>::apply( ::std::forward<function_type>( m_fn ), ::std::forward<params_type>( params ) );

			if ( pvarResult )
			{
				_variant_t ret = r;
				::VariantCopy( pvarResult, &ret );
			}
			return S_OK;
		}
	};
	template<typename...Args>
	class DispatchMethod < void( Args... ) >
		: public DispatchMethodBase
	{
	public:

		typedef std::function<void( Args... )> function_type;
		typedef void return_type;
		typedef return_type( *func_ptr )(Args...);
	private:
		typedef std::vector<_variant_t>		params_type;
		function_type				m_fn;

	public:
		DispatchMethod( DispatchMethod&& that ) :m_fn( std::move( that.m_fn ) ) {}
		DispatchMethod( const DispatchMethod& that ) :m_fn( that.m_fn ) {}

		DispatchMethod( function_type&& f ) :m_fn( std::move( f ) ) {}
		DispatchMethod( function_type& f ) :m_fn( f ) {}
		DispatchMethod( func_ptr f ) :m_fn( f ) {}

		~DispatchMethod()
		{}

		DispatchMethod& operator=(const DispatchMethod& _Right)
		{
			if ( this != &_Right )
			{
				this->m_fn = _Right.m_fn;
				this->m_id = _Right.m_id;
				this->m_name = _Right.m_name;
			}
			return (*this);
		}


		virtual HRESULT Execute( WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult = nullptr)
		{
			if ( pdispparams->cArgs < sizeof...(Args) )
			{
				return E_INVALIDARG;
			}
			params_type params;
			for ( int i = 0; i < sizeof...(Args); i++ )
			{
				params.push_back( pdispparams->rgvarg[ i ] );
			}
			Apply<sizeof...(Args)>::apply( ::std::forward<function_type>( m_fn ), ::std::forward<params_type>( params ) );
			return S_OK;
		}
	};
}